/*
x * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.wct;

import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bson.BSONObject;

import com.wct.db.DbUtils;
import com.wct.define.Constant;
import com.wct.jms.JMSMessage;
import com.wct.jms.Producer;
import com.wct.struct.GPSPacket;
import com.wct.struct.SyscfgPacket2;
import com.wct.util.GPSUtil;
import com.wct.util.PackageBuilder;
import com.wct.util.Util;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * Handler implementation for the echo server.
 */
@Sharable
public class EchoServerHandler extends ChannelHandlerAdapter {
	private final Logger logger = LogManager.getLogger(EchoServerHandler.class);
	private Producer producer;
	private PackageBuilder builder;
	private DbUtils dbUtils;
	private static int gpssize = new GPSPacket().size();
	private static int syscfgsize = new SyscfgPacket2().size();
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) {
		System.out.println("read data");
		// ctx.write(msg);
		ByteBuf in = (ByteBuf) msg;
		byte[] array = null;
		Boolean readtoArray = false;
		int index = 0;
	
			while (in.isReadable()) {
				if (readtoArray) {
					array[index] = in.readByte();
					index++;
				} else {
					byte temp = in.readByte();

					if (temp == 0x24) {
						readtoArray = true;
						array = new byte[in.readableBytes()+1];
						System.out.println("arraysize:" + array.length);
						System.out.println("index:" + index);
						System.out.println("in.readableBytes():" + in.readableBytes());
						array[index] = temp;
						index++;
					}
				}
			}
			
			if (array != null) {
				System.out.println("arraysize:" + array.length);
				System.out.println("array:" + Util.byte2hex(array));
				System.out.println("date:" + new Date());
				if (array.length == gpssize) {
					ByteBuffer buffer = ByteBuffer.wrap(array);

					GPSPacket packet = new GPSPacket();
					
					packet.setByteBuffer(buffer, 0);
					Boolean updatePower = true;
					if (Constant.battery_baseline.compareTo(packet.Data.Batterry.get())>0) {
						updatePower = false;
	                    sendBatteryPowerAlert(packet.DeviceSN.get() + "", Math.round(packet.Data.Batterry.get()));
                    }
					debugPackage(packet);
					
					if (packet.Cmd.get() == 1 && packet.CmdType.get() == 1){
						//gps
						handleGPSPackge(ctx, packet);
					}
					else if (packet.Cmd.get() == 0 && packet.CmdType.get() == 0) {
						//登陆包
						handleLoginPackge(ctx, packet);
					}
					else if (packet.Cmd.get() == 2 && packet.CmdType.get() == 0) {
						//心跳包
						handleKeepAlivePackge(ctx, packet, updatePower);// 回复SyscfgPacke
					}
					else if (packet.Cmd.get() == 3 && packet.CmdType.get() == 0) {
						//微信包
						//type = 1;// 不回复
						handleWeiXinPackge(ctx, packet);
					}
					else if (packet.Cmd.get() == 4 && packet.CmdType.get() == 0) {
						handleUrgencyPackge(ctx, packet);
						//紧急包
					}
					else if (packet.Cmd.get() == 1 && packet.CmdType.get() == 2) {
						// SmsPacket
						handleSMSPackge(ctx, packet);
					}
					else if (packet.Cmd.get() == 1 && packet.CmdType.get() == 0) {
						handleManualGPSPackge(ctx, packet);
					}
				}
				else if (array.length == syscfgsize) {
					ByteBuffer buffer = ByteBuffer.wrap(array);

					SyscfgPacket2 packet = new SyscfgPacket2();
					
					packet.setByteBuffer(buffer, 0);
					handleSysCfgPackge(ctx, packet);
				}	
			}

	}
	private void handleLoginPackge(ChannelHandlerContext ctx, GPSPacket packet){
		String identity = getIdentity(ctx);
		String deviceId = packet.DeviceSN.get()+"";
	    EchoServer.connectionMap.put(identity, ctx);
	    EchoServer.deviceid2ip.put(deviceId, identity);
	    EchoServer.ip2deviceid.put(identity, deviceId);
		dbUtils.updateDeviceOnlineStatus(deviceId, true);
		ByteBuffer buff = builder.MakeDefaultSyscfgFromDevice(deviceId);
		buff.position(0);
		ByteBuf resp = Unpooled.copiedBuffer(buff);
		ctx.write(resp);
		ctx.flush();
	}
	
	private void handleKeepAlivePackge(ChannelHandlerContext ctx, GPSPacket packet, Boolean updatePower){
		String deviceId = packet.DeviceSN + "";
		if (updatePower) {
			dbUtils.updateDevicePower(deviceId, Math.round(packet.Data.Batterry.get()));
        }	
		
		ByteBuffer buff = builder.MakeDefaultSyscfgFromDevice(deviceId);
		buff.position(0);
		ByteBuf resp = Unpooled.copiedBuffer(buff);
		ctx.write(resp);
		ctx.flush();
	}
	
	private void handleWeiXinPackge(ChannelHandlerContext ctx, GPSPacket packet){
		
	}
	
	private void handleUrgencyPackge(ChannelHandlerContext ctx, GPSPacket packet){
		JMSMessage message = new JMSMessage();
		message.setDeviceId(packet.DeviceSN.get()+"");
		message.setType(JMSMessage.Type.DEVICE_URGENCY);
		message.setDetail("设备发送紧急数据");
		producer.sendMessage(message);
		logger.debug("设备发送紧急数据");
		Double current_x= GPSUtil.convertToDouble(packet.Data.LonDegree.get(), packet.Data.LonCent.get(), packet.Data.LonSecond.get());
		Double current_y = GPSUtil.convertToDouble(packet.Data.LatDegree.get(), packet.Data.LatCent.get(), packet.Data.LatSecond.get());
		dbUtils.appendGPSRecord(packet, current_x, current_y);
	}
	
	private void sendBatteryPowerAlert(String deviceId, Integer power){
		dbUtils.updateDevicePower(deviceId, power);
		String detail = "设备电量报警，当前电量" + power + "%";
		logger.debug(detail);
		JMSMessage message = new JMSMessage();
		message.setDeviceId(deviceId);
		message.setType(JMSMessage.Type.BATTERY);
		message.setDetail(detail);
		producer.sendMessage(message);			
	}
	private void handleManualGPSPackge(ChannelHandlerContext ctx, GPSPacket packet){
		handleGPSPackge(ctx, packet);
	}
    private void handleGPSPackge(ChannelHandlerContext ctx, GPSPacket packet){
    	
    	List<BSONObject> fencelist = dbUtils.getFences(packet.DeviceSN.get()+"");
		Double current_x= GPSUtil.convertToDouble(packet.Data.LonDegree.get(), packet.Data.LonCent.get(), packet.Data.LonSecond.get());
		Double current_y = GPSUtil.convertToDouble(packet.Data.LatDegree.get(), packet.Data.LatCent.get(), packet.Data.LatSecond.get());
		logger.debug("current_x:" + current_x + " current_y: " + current_y);
    	if (fencelist!=null) {
	        for (BSONObject bsonObject : fencelist) {
	            Double x = (Double)bsonObject.get(Constant.Fence.x);
	            Double y = (Double)bsonObject.get(Constant.Fence.y);
	            String name = (String)bsonObject.get(Constant.Fence.name);
	            
	            long distance = GPSUtil.getDistance(current_x, current_y, x, y);
	            
	            int range = (Integer) bsonObject.get("range");
	            Boolean infence = (Boolean) bsonObject.get("infence");
	            String deviceid = packet.DeviceSN + "";
	            String detail = null;
	            Boolean currentFencestatus = false;
	            Boolean updateFence = false;
	            logger.debug("distance:" + distance + " current infence " + infence);
	            
	            if (infence!=null) {
	                if (infence && distance > range) {
	                	detail = "设备离开栅栏" + name;
	                	currentFencestatus = false;
	                	updateFence = true;
	                	
                    }
	                else if (!infence && distance <= range) {
	                	detail = "设备进入栅栏" + name;
	                	currentFencestatus = true;
	                	updateFence = true;              	 
					}
                }
	            else {
	            	if (distance > range) {
	            		detail = "设备离开栅栏" + name;
	                	currentFencestatus = false;
	                	updateFence = true;
                    }
	                else if (distance <= range) {
	                	detail = "设备进入栅栏" + name;
	                	currentFencestatus = true;
	                	updateFence = true;   
					}
				}
	            if (updateFence) {
	            	JMSMessage message = new JMSMessage();
            		message.setDeviceId(deviceid);
            		message.setType(JMSMessage.Type.NETTY_FENCEALERT);
            		message.setDetail(detail);
            		message.setAddition("栅栏数据 经度:" + x + ",纬度:" + y + ",范围:" + range + "米");
            		producer.sendMessage(message);
            		logger.debug(detail + ",infence:" + infence); 
            		
            		dbUtils.updateFence((String)bsonObject.get("_id"), currentFencestatus);
                }
            }
        }
		dbUtils.appendGPSRecord(packet, current_x, current_y);
	}
    
    private void handleSMSPackge(ChannelHandlerContext ctx, GPSPacket packet){
    	ByteBuffer buff = builder.makeSmsPacket(packet.DeviceSN+"");
		buff.position(0);
		ByteBuf resp = Unpooled.copiedBuffer(buff);
		ctx.write(resp);
		ctx.flush();
   	}
    
    private void handleSysCfgPackge(ChannelHandlerContext ctx, SyscfgPacket2 packet){
    	logger.debug("sms mesage");
		dbUtils.updateDeviceinfo(packet);
   	}
//	@Override
//	public void channelReadComplete(ChannelHandlerContext ctx) throws UnsupportedEncodingException {
//	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		// Close the connection when an exception is raised.
		cause.printStackTrace();
		String deviceid = removeContext(ctx);
		
		JMSMessage message = new JMSMessage();
		message.setDeviceId(deviceid);
		message.setType(JMSMessage.Type.OFFLINE);
		message.setDetail("设备处于离线状态");
		producer.sendMessage(message);
		System.out.println("disconnect");
		ctx.close();
	}

	public Producer getProducer() {
		return producer;
	}

	public void setProducer(Producer producer) {
		this.producer = producer;
	}

	public PackageBuilder getBuilder() {
		return builder;
	}

	public void setBuilder(PackageBuilder builder) {
		this.builder = builder;
	}

	public DbUtils getDbUtils() {
		return dbUtils;
	}

	public void setDbUtils(DbUtils dbUtils) {
		this.dbUtils = dbUtils;
	}
	
	private void debugPackage(GPSPacket packet){
		String temp = "\n";
		temp = temp + "StartId:" + packet.StartId.get() + "\n";
		temp = temp + "Cmd:" + packet.Cmd.get() + "\n";
		temp = temp + "CmdType:" + packet.CmdType.get() + "\n";
		temp = temp + "DataLen:" + packet.DataLen.get() + "\n";
		temp = temp + "DeviceSN:" + packet.DeviceSN.get() + "\n";
		temp = temp + "EndID:" + packet.EndId.get() + "\n";
//		temp = temp + "Data.Time.year:" + packet.Data.Time.year.get() + "\n";
//		temp = temp + "Data.Time.month:" + packet.Data.Time.month.get() + "\n";
//		temp = temp + "Data.Time.day:" + packet.Data.Time.day.get() + "\n";
//		temp = temp + "Data.Time.hour:" + packet.Data.Time.hour.get() + "\n";
//		temp = temp + "Data.Time.minute:" + packet.Data.Time.minute.get() + "\n";
//		temp = temp + "Data.Time.second:" + packet.Data.Time.second.get() + "\n";
		temp = temp + "Data.LatDegree:" + packet.Data.LatDegree.get() + "\n";
		temp = temp + "Data.LatCent:" + packet.Data.LatCent.get() + "\n";
		temp = temp + "Data.LatSecond:" + packet.Data.LatSecond.get() + "\n";
		temp = temp + "Data.LonDegree:" + packet.Data.LonDegree.get() + "\n";
		temp = temp + "Data.LonCent:" + packet.Data.LonCent.get() + "\n";
		temp = temp + "Data.LonSecond:" + packet.Data.LonSecond.get() + "\n";
		temp = temp + "Data.Speed:" + packet.Data.Speed.get() + "\n";
		temp = temp + "Data.Direct:" + packet.Data.Direct.get() + "\n";
		temp = temp + "Data.heightGgroud:" + packet.Data.heightGgroud.get() + "\n";
		temp = temp + "Data.HeightSea:" + packet.Data.HeightSea.get() + "\n";
		temp = temp + "Data.NS:" + packet.Data.NS.get() + "\n";
		temp = temp + "Data.HeightSea:" + packet.Data.EW.get() + "\n";
//		temp = temp + "CfgData.bDevice:" + packet.CfgData.bDevice.get() + "\n";
//		temp = temp + "CfgData.bMusic:" + packet.CfgData.bMusic.get() + "\n";
//		temp = temp + "CfgData.CtrlNumber:" + packet.CfgData.CtrlNumber + "\n";
//		temp = temp + "CfgData.UrgentNumber:" + packet.CfgData.UrgentNumber + "\n";
//		temp = temp + "CfgData.InterVal:" + packet.CfgData.InterVal.get() + "\n";
//		temp = temp + "CfgData.Mode:" + packet.CfgData.Mode.get() + "\n";
		logger.debug(temp);
	}
	
	private String getIdentity(ChannelHandlerContext ctx){
		InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
		Integer port = insocket.getPort();
	    String clientIP = insocket.getAddress().getHostAddress();
	    return clientIP + ":" + port;
	}
	
	private String removeContext(ChannelHandlerContext ctx){
		String identity = getIdentity(ctx);
		if (EchoServer.connectionMap != null && EchoServer.connectionMap.containsKey(identity)) {
			EchoServer.connectionMap.remove(identity);
		    String devicdid = EchoServer.ip2deviceid.get(identity);
		    EchoServer.ip2deviceid.remove(identity);	    
		    EchoServer.deviceid2ip.remove(devicdid);
		    dbUtils.updateDeviceOnlineStatus(devicdid, false);
		    return devicdid;
		}
		else {
			return null;
		}
	    
	   
	}
	
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception{
		String deviceid = removeContext(ctx);
	    if (deviceid!=null) {
	    	JMSMessage message = new JMSMessage();
			message.setDeviceId(deviceid);
			message.setType(JMSMessage.Type.OFFLINE);
			message.setDetail("设备处于离线状态");
			producer.sendMessage(message);			
        }
	    System.out.println("disconnect");
	    super.channelInactive(ctx);
	}
}
